package com.terminus.test.filter;

import com.terminus.test.cipher.Cipherer;
import com.terminus.test.probability.HitProbability;
import com.terminus.test.split.SplitModel;
import com.terminus.test.split.SplitUtil;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class BloomFilter {

    private int split;
    private Map<Integer,BitSet> mapFilter;
    private double probability;
    private SplitModel splitModel;
    private int k;
    private int n;
    private int lap;

    private int BIT_SET_MAX=0x000fffff;
    public BloomFilter(int split,double probability) {

        this.mapFilter=new ConcurrentHashMap<>();
        this.split=split;
        this.splitModel=SplitUtil.getOptimalModel(split);
        this.k=this.splitModel.getK();
        this.n=this.splitModel.getN();
        this.lap=this.splitModel.getLap();
        this.probability=probability;
        for (int i = 1; i <=this.split ; i++) {
            this.mapFilter.put(i,new BitSet(BIT_SET_MAX));
        }
    }

    private int hashSlice(String slice){
        return Math.abs(slice.hashCode())%BIT_SET_MAX;
    }

    public boolean isVisited(String url){
        List<Boolean> results = new ArrayList<>();
        this.splitUrl(url).forEach((key, value) -> results.add(this.mapFilter.get(key).get(hashSlice(value))));
        for (boolean result : results) {
            if (!result) {
                return false;
            }
        }
        return HitProbability.hit(probability);
    }

    public void visited(String url){
        this.splitUrl(url).forEach((key,value)-> this.mapFilter.get(key).set(this.hashSlice(value)));
    }

    private Map<Integer,String> splitUrl(String url){
        return splitUrl(Cipherer.encryptMD5(url),this.k,this.n,this.lap);
    }
    private Map<Integer,String> splitUrl(String cipherText, int k,int n,int lap){
        Map<Integer,String> map=new ConcurrentHashMap<>();
        int start=0;
        for (int i = 1; i <=k ; i++) {
            map.put(i,cipherText.substring(start,start+k));
            start+=n;
            start-=lap;
        }
        return map;
    }
}
